home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
programr
/
msjv6-5.zip
/
WINDOS.ZIP
/
WINIO.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-09-01
|
34KB
|
990 lines
/*
WINIO.C
Stdio (e.g. printf) functionality for Windows - implementation
Dave Maxey - 1991
revisions by Andrew Schulman - 1991
originally in Microsoft Systems Journal, July 1991
revised for use by article in MSJ, September 1991
*/
#include <windows.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#include <string.h>
#include "wmhandlr.h"
#include "winio.h"
/* PROTOTYPES in alphabetic order */
void addchars(BYTE *, unsigned);
void adjust_caret(void);
void append2buffer(BYTE *, unsigned);
int chInput(void);
void compute_repaint(void);
int initialize_buffers(unsigned);
int initialize_class(HANDLE);
void initialize_state(void);
int initialize_window(HANDLE, HANDLE, int);
void make_avail(unsigned);
BYTE far * nextline(BYTE far *);
BYTE far * prevline(BYTE far *);
void set_font(void);
long winio_wmpaint(HWND, unsigned, WORD, LONG);
long winio_wmsize(HWND, unsigned, WORD, LONG);
long winio_wmdestroy(HWND, unsigned, WORD, LONG);
long winio_wmchar(HWND, unsigned, WORD, LONG);
long winio_wmkeydown(HWND, unsigned, WORD, LONG);
long winio_wmhscroll(HWND, unsigned, WORD, LONG);
long winio_wmvscroll(HWND, unsigned, WORD, LONG);
long winio_wmsetfocus(HWND, unsigned, WORD, LONG);
long winio_wmkillfocus(HWND, unsigned, WORD, LONG);
// this doesn't get declared in stdio.h if _WINDOWS is defined
// although it is in the Windows libraries!
int vsprintf(char *, const char *, va_list);
#define winio_caret_visible() \
((yCurrLine <= (yTopOfWin + yWinHeight)) && \
(xCurrPos <= (xLeftOfWin + xWinWidth)) && \
(xCurrPos >= xLeftOfWin))
#define CHECK_INIT() if (! tWinioVisible) return FALSE
#define MAX_X 127
#define TABSIZE 8
#define TYPE_AHEAD 256
#define WINIO_DEFAULT_BUFFER 8192
#define MIN_DISCARD 256
#define CARET_WIDTH 2
// For scrolling procedures
#define USE_PARAM 10000
#define DO_NOTHING 10001
BYTE winio_class[15] = "winio_class";
BYTE winio_icon[15] = "winio_icon";
BYTE winio_title[128] = "Stdio Window";
unsigned long bufsize = WINIO_DEFAULT_BUFFER;
unsigned long kbsize = TYPE_AHEAD;
unsigned bufused, bufSOI;
unsigned curr_font = SYSTEM_FIXED_FONT;
int tWinioVisible = FALSE;
int tCaret = FALSE, tFirstTime = TRUE;
int cxChar, cyChar, cxScroll, cyScroll, cxWidth, cyHeight;
int xWinWidth, yWinHeight, xCurrPos;
int xLeftOfWin, yTopOfWin, yCurrLine;
unsigned pchKbIn, pchKbOut;
static BYTE far *fpBuffer, far *fpTopOfWin, far *fpCurrLine;
static BYTE far *fpKeyboard;
static HANDLE hBuffer, hKeyboard;
static HWND hwnd;
BOOL tTerminate = TRUE,
tPaint = TRUE,
tEcho = TRUE;
DESTROY_FUNC destroy_func;
typedef struct {
int hSB, vSB;
} recVKtoSB;
/* This table defines, by scroll message, what increment to try */
/* and scroll horizontally. PGUP and PGDN entries are updated */
/* in the winio_wmsize function. */
int cScrollLR[SB_ENDSCROLL + 1] =
//UP DOWN PGUP PGDN POS TRACK TOP BOT ENDSCROLL
{ -1, +1, -1, +1, USE_PARAM, USE_PARAM, -MAX_X, MAX_X, DO_NOTHING};
/* This table defines, by scroll message, what increment to try */
/* and scroll horizontally. PGUP and PGDN entries are updated */
/* in the winio_wmsize function, and the TOP & BOTTOM entries */
/* are updated by addchar function. */
int cScrollUD[SB_ENDSCROLL + 1] =
//UP DOWN PGUP PGDN POS TRACK TOP BOT ENDSCROLL
{ -1, +1, -1, +1, USE_PARAM, USE_PARAM, -1, +1, DO_NOTHING};
/* This table associates horizontal and vertical scroll */
/* messages that should be generated by the arrow and page keys */
recVKtoSB VKtoSB[VK_DOWN - VK_PRIOR + 1] =
// VK_PRIOR VK_NEXT
{ { DO_NOTHING, SB_PAGEUP }, { DO_NOTHING, SB_PAGEDOWN },
// VK_END VK_HOME
{ SB_TOP, SB_BOTTOM }, { SB_TOP, SB_TOP },
// VK_LEFT VK_UP
{ SB_LINEUP, DO_NOTHING }, { DO_NOTHING, SB_LINEUP },
// VK_RIGHT VK_DOWN
{ SB_LINEDOWN, DO_NOTHING },{ DO_NOTHING, SB_LINEDOWN } };
/* =================================================================== */
/* the interface functions themselves..... */
/* =================================================================== */
char *gets(char *pchTmp)
{
BYTE *pch = pchTmp;
int c;
CHECK_INIT();
bufSOI = bufused; /* mark beginning of line to limit backspace */
do {
if ((c = fgetchar()) == '\n')
c = '\0';
switch (c)
{
case '\b' : if (pch > pchTmp) pch--; break;
case 0x1b : pch = pchTmp; break;
case EOF : bufSOI = -1; return NULL;
default : *pch = (BYTE) c; pch++;
}
} while (c);
bufSOI = -1;
return pchTmp;
}
int printf(const char *fmt, ...)
{
va_list marker;
va_start(marker, fmt);
return vprintf(fmt, marker);
}
int vprintf(const char *fmt, va_list marker)
{
static BYTE s[1024];
int len;
CHECK_INIT();
len = vsprintf(s, fmt, marker);
addchars(s,len);
return len;
}
int fgetchar(void)
{
int ch;
CHECK_INIT();
ch = chInput();
if (tEcho)
fputchar(ch);
return ch;
}
int kbhit(void)
{
CHECK_INIT();
return (pchKbIn == pchKbOut);
}
int fputchar(int c)
{
CHECK_INIT();
addchars(&((char) c), 1);
return c;
}
int puts(const char *s)
{
BYTE c = '\n';
CHECK_INIT();
addchars((BYTE *) s, strlen(s));
addchars(&c, 1);
return 0;
}
/* --------------------------------------------------------------- */
/* USED INTERNALLY - pops up an error window and returns FALSE */
/* --------------------------------------------------------------- */
int fail(BYTE *s)
{
MessageBox(NULL,s,"ERROR",MB_OK);
return FALSE;
}
/* --------------------------------------------------------------- */
/* pops up a message window */
/* --------------------------------------------------------------- */
BOOL winio_warn(BOOL confirm, const BYTE *fmt, ...)
{
BYTE s[256];
va_list marker;
va_start(marker, fmt);
vsprintf(s, fmt, marker);
va_end(marker);
return (MessageBox(NULL, s, winio_title,
confirm? MB_OKCANCEL : MB_OK) == IDOK);
}
/* --------------------------------------------------------------- */
/* The application must call this function before using any of the */
/* covered stdio type calls. We need the parameter info in order */
/* to create the window. The function allocates the buffer and */
/* creates the window. It returns TRUE or FALSE. */
/* --------------------------------------------------------------- */
int winio_init(HANDLE hInstance, HANDLE hPrevInstance,
int nCmdShow, unsigned wBufSize)
{
if (tWinioVisible)
return FALSE;
if (! initialize_buffers(wBufSize))
return FALSE;
initialize_state();
if (! initialize_window(hInstance, hPrevInstance, nCmdShow))
return FALSE;
tWinioVisible = TRUE;
atexit(winio_end); /* hook into exit chain */
winio_yield();
return TRUE;
}
/* --------------------------------------------------------------- */
/* Clear the contents of the buffer. */
/* --------------------------------------------------------------- */
void winio_clear(void)
{
_fmemset(fpBuffer,0,(int) bufsize - 1);
fpCurrLine = fpTopOfWin = fpBuffer;
*fpBuffer = '\0';
xCurrPos = 0;
yCurrLine = 0;
yTopOfWin = 0;
xLeftOfWin = 0;
bufused = 0;
if (tWinioVisible)
{
SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
}
}
/* --------------------------------------------------------------- */
/* Return the window handle of the underlying Windows object. */
/* Can be used by an application to customize the WINIO window */
/* --------------------------------------------------------------- */
HWND winio_hwnd(void)
{
return hwnd;
}
/* --------------------------------------------------------------- */
/* This function is called by winio_init(). It initializes a number */
/* of global variables, including the WM_ handler table. */
/* --------------------------------------------------------------- */
void initialize_state()
{
winio_clear();
destroy_func = 0;
/* set up our message handlers */
wmhandler_init();
wmhandler_set(WM_PAINT, winio_wmpaint);
wmhandler_set(WM_SIZE, winio_wmsize);
wmhandler_set(WM_DESTROY, winio_wmdestroy);
wmhandler_set(WM_CHAR, winio_wmchar);
wmhandler_set(WM_HSCROLL, winio_wmhscroll);
wmhandler_set(WM_VSCROLL, winio_wmvscroll);
wmhandler_set(WM_SETFOCUS, winio_wmsetfocus);
wmhandler_set(WM_KILLFOCUS, winio_wmkillfocus);
wmhandler_set(WM_KEYDOWN, winio_wmkeydown);
}
/* --------------------------------------------------------------- */
/* This function is called by winio_init(). It initializes our */
/* Windows class, and some global variables */
/* --------------------------------------------------------------- */
int initialize_window(HANDLE hInst, HANDLE hPrev, int nCmdShow)
{
static RECT start;
int cx, cy, inc;
cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);
inc = GetSystemMetrics(SM_CYCAPTION);
cxScroll = GetSystemMetrics(SM_CXVSCROLL);
cyScroll = GetSystemMetrics(SM_CYHSCROLL);
if (hPrev)
{
// note: other WINIO apps are NOT other instances!
GetInstanceData(hPrev, (NPSTR) &start, sizeof(RECT));
start.top += inc;
start.left += inc;
if (start.top > (cy >> 2))
start.top = cy >> 3;
if (start.left > (cx >> 2))
start.left = cx >> 3;
}
else
{
if (! initialize_class(hInst))
return fail("Could not create class");
start.left = cx >> 3;
start.right = 3 * (cx >> 2);
start.top = cy >> 3;
start.bottom = 3 * (cy >> 2);
}
hwnd = CreateWindow(winio_class, winio_title,
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
start.left, start.top, start.right, start.bottom,
NULL, NULL, hInst, NULL);
if (! hwnd)
return fail("Could not create window");
set_font();
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
return TRUE;
}
/* ----------------------------------------------------------------------- */
/* Initializes Window Class */
/* ----------------------------------------------------------------------- */
int initialize_class(HANDLE hInst)
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, winio_icon);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = winio_class;
return RegisterClass(&wc);
}
/* ----------------------------------------------------------------------- */
/* Uses GlobalAlloc() to allocate the display and keyboard buffers */
/* ----------------------------------------------------------------------- */
int initialize_buffers(unsigned wBufSize)
{
if (wBufSize)
bufsize = max(wBufSize, 1024);
if (! (hBuffer = GlobalAlloc(GMEM_MOVEABLE, bufsize)))
return fail("Could not allocate/nconsole I/O buffer");
fpBuffer = GlobalLock(hBuffer); // keep locked; assume protected mode
if (! (hKeyboard = GlobalAlloc(GMEM_MOVEABLE, kbsize)))
return fail("Could not allocate/type ahead buffer");
fpKeyboard = GlobalLock(hKeyboard);
*fpBuffer = '\0';
fpBuffer++;
return TRUE;
}
/* ----------------------------------------------------------------------- */
/* Undoes the work of the above. Allows an application to close the window */
/* Terminates the prog. */
/* ----------------------------------------------------------------------- */
void winio_end()
{
while (tWinioVisible)
winio_yield();
}
/* ------------------------------------------------------------------- */
/* Closes the window by sending it a WM_DESTROY message. Note that it */
/* does not disable the _onclose defined function. So the user program */
/* handler will be triggered. Does NOT cause the app. to terminate. */
/* ------------------------------------------------------------------- */
void winio_close()
{
tTerminate = FALSE;
DestroyWindow(hwnd);
tTerminate = TRUE;
}
/* ------------------------------------------------------------------- */
/* processes any outstanding events waiting. These may be characters */
/* typed at the keyboard, WM_PAINT messages, etc. It is called */
/* internally but should also be used liberally by the application */
/* within loops. */
/* ------------------------------------------------------------------- */
void winio_yield()
{
MSG msg;
CHECK_INIT();
while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/* ------------------------------------------------------------------- */
/* Let the application install an exit routine, called back from */
/* winio_wmdestroy(). Deinstall by winio_onclose(NULL) */
/* ------------------------------------------------------------------- */
void winio_onclose(DESTROY_FUNC exitfunc)
{
destroy_func = exitfunc;
}
/* ------------------------------------------------------------------- */
/* This function allows the font of the window to be modified, and may */
/* be used BEFORE winio_init. Currently, only SYSTEM_, ANSI_, and */
/* OEM_FIXED_FONTs are supported. */
/* ------------------------------------------------------------------- */
BOOL winio_setfont(WORD wFont)
{
if ((wFont != SYSTEM_FIXED_FONT) &&
(wFont != ANSI_FIXED_FONT) &&
(wFont != OEM_FIXED_FONT))
return FALSE;
curr_font = wFont;
if (tWinioVisible)
{
set_font();
if (tPaint)
InvalidateRect(hwnd, NULL, TRUE);
}
return TRUE;
}
/* ------------------------------------------------------------------- */
/* This function allows the title of the window to be modified, and may */
/* be used BEFORE winio_init. */
/* ------------------------------------------------------------------- */
void winio_settitle(BYTE *pchTitle)
{
strncpy(winio_title, pchTitle, 127);
winio_title[127] = '\0';
if (tWinioVisible)
SetWindowText(hwnd, winio_title);
}
/* ------------------------------------------------------------------- */
/* This function allows the caller to specifiy immediate or deferred */
/* screen updates. The call may not be issued before winio_init(). */
/* ------------------------------------------------------------------- */
BOOL winio_setpaint(BOOL on)
{
BOOL ret = tPaint;
CHECK_INIT();
if (tPaint = on)
InvalidateRect(hwnd, NULL, TRUE);
return ret;
}
/* ------------------------------------------------------------------- */
/* This function changes the behavior of getchar(), whose default */
/* is to echo characters, unlike DOS. winio_setecho(FALSE) restores */
/* the non-echo DOS behavior. */
/* ------------------------------------------------------------------- */
BOOL winio_setecho(BOOL flag)
{
BOOL ret = tEcho;
tEcho = flag;
return ret;
}
/* --------------------------------------------------------------- */
/* Our WM_PAINT handler. It sends the currrent 'in view' piece of */
/* the buffer to the window. Note that an embedded NULL character */
/* signifies an end of line, not '\n'. */
/* --------------------------------------------------------------- */
long winio_wmpaint(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
HDC hdc;
PAINTSTRUCT ps;
BYTE far *pchSOL = fpTopOfWin;
BYTE far *pchEOL;
int i, j, xStart;
int xLeft, xRight, yTop, yBottom;
hdc = BeginPaint(hwnd, &ps);
xLeft = (ps.rcPaint.left / cxChar) + xLeftOfWin;
xRight = (ps.rcPaint.right / cxChar) + xLeftOfWin;
yTop = ps.rcPaint.top / cyChar;
yBottom = ps.rcPaint.bottom / cyChar;
SelectObject(hdc, GetStockObject(curr_font));
for (i = 0; i < yTop; i++) // lines above repaint region
{
while (*pchSOL)
pchSOL++;
pchSOL++;
}
if (i <= yCurrLine) // something needs repainting..
{
for (i = yTop; i <= yBottom; i++) // lines in repaint region
{
for (j = 0; (j < xLeft) && (*pchSOL); j++, pchSOL++)
; // Scroll right
pchEOL = pchSOL;
xStart = j - xLeftOfWin;
for (j = 0; (*pchEOL) ; j++, pchEOL++) ; // end of line
TextOut(hdc, cxChar * xStart, cyChar * i, pchSOL,
min(j, xRight - xLeft + 2));
if ((unsigned)(pchEOL - fpBuffer) >= bufused)
break;
pchSOL = ++pchEOL;
}
}
EndPaint(hwnd, &ps);
adjust_caret();
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_SIZE handler. It updates the internal record of our */
/* window size, minus the scroll bars, and recalcs the scroll bar */
/* ranges. */
/* --------------------------------------------------------------- */
long winio_wmsize(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
cxWidth = LOWORD(lParam);
cyHeight = HIWORD(lParam);
xWinWidth = (cxWidth - cxScroll ) / cxChar;
yWinHeight = (cyHeight - cyScroll ) / cyChar;
cScrollLR[SB_PAGEUP] = -xWinWidth / 2;
cScrollLR[SB_PAGEDOWN] = +xWinWidth / 2;
cScrollUD[SB_PAGEUP] = -yWinHeight + 1;
cScrollUD[SB_PAGEDOWN] = +yWinHeight - 1;
SetScrollRange(hwnd, SB_HORZ, 1, MAX_X, FALSE);
SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_DESTROY handler. It frees up storage associated with the */
/* window, and resets its state to uninitialized. */
/* --------------------------------------------------------------- */
long winio_wmdestroy(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
if (destroy_func)
(*destroy_func)();
GlobalUnlock(hBuffer);
GlobalUnlock(hKeyboard);
GlobalFree(hBuffer);
GlobalFree(hKeyboard);
tWinioVisible = FALSE;
if (tTerminate) exit(0);
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_BYTE handler. It adds the BYTE to the internal kb buffer */
/* if there is room otherwise it queues a BEEP */
/* --------------------------------------------------------------- */
long winio_wmchar(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
BYTE far *lpchKeybd = fpKeyboard;
unsigned pchSave = pchKbIn;
pchKbIn++;
if (pchKbIn == TYPE_AHEAD)
pchKbIn = 0;
if (pchKbIn == pchKbOut)
{
MessageBeep(0);
pchKbIn = pchSave;
}
else
*(lpchKeybd + pchSave) = LOBYTE(wParam);
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_KEYDOWN handler. This handles what would be called */
/* function keys in the DOS world. In this case the function keys */
/* operate as scroll bar controls, so we generate messages to the */
/* scroll message handlers below. */
/* --------------------------------------------------------------- */
long winio_wmkeydown(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
int hSB, vSB;
if ((wParam < VK_PRIOR) || (wParam > VK_DOWN))
return 0;
hSB = VKtoSB[wParam - VK_PRIOR].hSB;
vSB = VKtoSB[wParam - VK_PRIOR].vSB;
if (hSB != DO_NOTHING)
SendMessage(hwnd, WM_HSCROLL, hSB, 0L);
if (vSB != DO_NOTHING)
SendMessage(hwnd, WM_VSCROLL, vSB, 0L);
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_HSCROLL handler. It adjusts what part of the buffer */
/* is visible. It operates as left/right arrow keys. */
/* --------------------------------------------------------------- */
long winio_wmhscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
int cxSave = xLeftOfWin,
xInc = cScrollLR[wParam];
if (xInc == DO_NOTHING)
return 0;
else if (xInc == USE_PARAM)
xLeftOfWin = LOWORD(lParam) - 1;
else
xLeftOfWin += xInc;
if ((xLeftOfWin = max(0, min(MAX_X - 1, xLeftOfWin))) == cxSave)
return 0;
ScrollWindow(hwnd, (cxSave - xLeftOfWin) * cxChar, 0, NULL, NULL);
SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
UpdateWindow(hwnd);
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_VSCROLL handler. It adjusts what part of the buffer */
/* is visible. It operates as page and line up/down keys. */
/* --------------------------------------------------------------- */
long winio_wmvscroll(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
int cySave = yTopOfWin,
yInc = cScrollUD[wParam],
i;
if (yInc == DO_NOTHING)
return 0;
else if (yInc == USE_PARAM)
yTopOfWin = LOWORD(lParam) - 1;
else
yTopOfWin += yInc;
if ((yTopOfWin = max(0, min(yCurrLine, yTopOfWin))) == cySave)
return 0;
if (yTopOfWin > cySave)
for (i = cySave; i < yTopOfWin; i++)
fpTopOfWin = nextline(fpTopOfWin);
else
for (i = cySave; i > yTopOfWin; i--)
fpTopOfWin = prevline(fpTopOfWin);
ScrollWindow(hwnd, 0, (cySave - yTopOfWin) * cyChar, NULL, NULL);
SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
UpdateWindow(hwnd);
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_SETFOCUS handler. It sets up the text caret. */
/* --------------------------------------------------------------- */
long winio_wmsetfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
CreateCaret(hwnd, NULL, CARET_WIDTH, cyChar);
if ((tCaret = winio_caret_visible()))
{
SetCaretPos((xCurrPos - xLeftOfWin) * cxChar,
(yCurrLine - yTopOfWin) * cyChar);
ShowCaret(hwnd);
}
return 0;
}
/* --------------------------------------------------------------- */
/* Our WM_KILLFOCUS handler. It destroys the text caret. */
/* --------------------------------------------------------------- */
long winio_wmkillfocus(HWND hwnd, unsigned message, WORD wParam, LONG lParam)
{
if (tCaret)
{
HideCaret(hwnd);
tCaret = FALSE;
}
DestroyCaret();
return 0;
}
void set_font(void)
{
HDC hdc;
TEXTMETRIC tm;
hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(curr_font));
GetTextMetrics(hdc,&tm);
ReleaseDC(hwnd,hdc);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight+tm.tmExternalLeading;
xWinWidth = (cxWidth - cxScroll ) / cxChar;
yWinHeight = (cyHeight - cyScroll ) / cyChar;
}
/* --------------------------------------------------------------- */
/* Adjusts the position of the caret, and shows or hides it, as */
/* appropriate. */
/* --------------------------------------------------------------- */
void adjust_caret()
{
int t = winio_caret_visible();
if (t)
SetCaretPos((xCurrPos - xLeftOfWin) * cxChar,
(yCurrLine - yTopOfWin) * cyChar);
if (t && (! tCaret))
ShowCaret(hwnd);
if ((! t) && tCaret)
HideCaret(hwnd);
tCaret = t;
}
/* --------------------------------------------------------------- */
/* Computes, on the basis of what has just been updated, what area */
/* of the window needs to be repainted. */
/* --------------------------------------------------------------- */
void compute_repaint(void)
{
RECT rc;
static int xCP = 0, yCL = 0;
int tWholeWin = FALSE;
if (yCurrLine > (yTopOfWin + yWinHeight))
{
yTopOfWin = 0;
fpTopOfWin = fpBuffer;
while (yTopOfWin < (yCurrLine - ((yWinHeight + 1) / 2)))
{
fpTopOfWin = nextline(fpTopOfWin);
yTopOfWin++;
}
tWholeWin = TRUE;
}
if ((xCurrPos < xLeftOfWin) || (xCurrPos > (xLeftOfWin + xWinWidth)))
{
xLeftOfWin = 0;
while (xLeftOfWin < (xCurrPos - ((xWinWidth + 1) / 2)))
xLeftOfWin++;
tWholeWin = TRUE;
}
if (tWholeWin)
InvalidateRect(hwnd, NULL, TRUE);
else
{
rc.left = ((yCL == yCurrLine) ?
(min(xCP, xCurrPos) - xLeftOfWin) * cxChar : 0);
rc.top = (yCL - yTopOfWin) * cyChar;
rc.right = (xWinWidth + 1) * cxChar;
rc.bottom = (yCurrLine - yTopOfWin + 1) * cyChar;
InvalidateRect(hwnd, &rc, TRUE);
}
yCL = yCurrLine;
xCP = xCurrPos;
}
/* --------------------------------------------------------------- */
/* Adds the supplied cch-long string to the display buffer, and */
/* ensures any changed part of the window is repainted. */
/* --------------------------------------------------------------- */
void addchars(BYTE *pch, unsigned cch)
{
int ycSave = yCurrLine;
int ytSave = yTopOfWin;
int xSave = xLeftOfWin;
make_avail(cch);
append2buffer(pch, cch);
if (ycSave != yCurrLine)
SetScrollRange(hwnd, SB_VERT, 1, yCurrLine + 1, FALSE);
if (! tPaint)
return;
compute_repaint();
cScrollUD[SB_TOP] = -yCurrLine;
cScrollUD[SB_BOTTOM] = yCurrLine;
if (ytSave != yTopOfWin)
SetScrollPos(hwnd, SB_VERT, yTopOfWin + 1, TRUE);
if (xSave != xLeftOfWin)
SetScrollPos(hwnd, SB_HORZ, xLeftOfWin + 1, TRUE);
winio_yield();
}
/* --------------------------------------------------------------- */
/* Add chars onto the display buffer, wrapping at end of line, */
/* expanding tabs, etc. */
/* --------------------------------------------------------------- */
void append2buffer(BYTE *pch, unsigned cch)
{
unsigned i;
for (i = 0; i < cch; i++, pch++)
{
switch (*pch)
{
case '\n' :
*pch = '\0';
*(fpBuffer + bufused) = '\0';
bufused++;
fpCurrLine = fpBuffer + bufused;
yCurrLine++;
xCurrPos = 0;
bufSOI = bufused;
break;
case '\t' :
do {
*(fpBuffer + bufused) = ' ';
bufused++;
xCurrPos++;
} while ((xCurrPos % TABSIZE) != 0);
break;
case EOF :
break;
case '\b' :
if (bufused > bufSOI)
{
bufused--;
xCurrPos--;
}
break;
case 0x1b :
while (bufused > bufSOI)
{
bufused--;
xCurrPos--;
}
break;
case 0x07 :
MessageBeep(0);
break;
default :
if (*pch > 0x1a)
{
if (xCurrPos >= MAX_X)
{
*(fpBuffer + bufused) = '\0';
bufused++;
xCurrPos = 0;
yCurrLine++;
fpCurrLine = fpBuffer + bufused;
}
xCurrPos++;
*(fpBuffer + bufused) = *pch;
bufused++;
}
}
}
*(fpBuffer + bufused) = '\0'; // '\0' terminator after end of buffer
}
/* --------------------------------------------------------------- */
/* If we have run out of room in the display buffer, drop whole */
/* lines, and move the remaining buffer up. */
/* --------------------------------------------------------------- */
void make_avail(unsigned cch)
{
unsigned cDiscard = 0;
BYTE far *fpTmp;
unsigned i;
if ((unsigned long)(bufused + cch + TABSIZE) < bufsize)
return;
fpTmp = fpBuffer;
cDiscard = ((max(MIN_DISCARD, cch + 1) + MIN_DISCARD - 1)
/ MIN_DISCARD) // this gives a whole number of
* MIN_DISCARD; // our allocation units.
fpTmp += (LONG) cDiscard;
fpTmp = nextline(fpTmp);
cDiscard = fpTmp - fpBuffer;
_fmemcpy(fpBuffer, fpTmp, bufused - cDiscard + 1);
bufused -= cDiscard;
if ((int) bufSOI != -1) bufSOI -= cDiscard;
fpTmp = fpBuffer + (LONG) bufused;
for (i = 0; i < cDiscard; i++) *fpTmp++ = '\0';
fpCurrLine = fpBuffer;
xCurrPos = yCurrLine = 0;
for (i = 0; i < bufused; i++)
{
if (*fpCurrLine)
xCurrPos++;
else
{
xCurrPos = 0;
yCurrLine++;
}
fpCurrLine++;
}
xLeftOfWin = yTopOfWin = -9999;
InvalidateRect(hwnd, NULL, TRUE);
}
/* ------------------------------------------------------------------- */
/* These two routines find the beginning of the next, and previous */
/* lines relative to their input pointer */
/* ------------------------------------------------------------------- */
BYTE far *nextline(BYTE far *p) { while (*p) p++; return ++p; }
BYTE far *prevline(BYTE far *p) { p--; do p--; while (*p); return ++p; }
/* ------------------------------------------------------------------- */
/* Waits for a character to appear in the keyboard buffer, yielding */
/* while nothing is available. Then inserts it into the buffer. */
/* ------------------------------------------------------------------- */
int chInput(void)
{
BYTE far *lpchKeyBd;
BYTE c;
CHECK_INIT();
while (pchKbIn == pchKbOut)
winio_yield();
lpchKeyBd = fpKeyboard;
c = *(lpchKeyBd + pchKbOut);
pchKbOut++;
if (pchKbOut == TYPE_AHEAD)
pchKbOut = 0;
// Do CR/LF and EOF translation
return (c == 0x1a) ? EOF : (c == '\r') ? '\n' : c;
}